﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom;
using System.Reflection;
using System.Text.RegularExpressions;
using devtm.Editor.TypeSelector;
using System.Collections.ObjectModel;
using devtm.AutoMapper.CustomCode;
using CodeDomHelper.Types;
using CodeDomHelper.CodeDom;
using System.ComponentModel;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel.Composition;



namespace devtm.AutoMapper
{

    [CLSCompliant(false)]
    public class AutoMapperCodeDomGenerator
    {


        private string AutoMapperName = string.Empty;
        This MyThis = new This(new TypeExp(typeof(object)));

        VariableExp VariableAutomapper;

        private Dictionary<string, IndexBag> IndexexDic = new Dictionary<string, IndexBag>();

        PropertyExp PerCounterClass;
        PropertyExp PerCounterProperties;

        MethodInvoke PerCounterIncrementClass;
        MethodInvoke PerCounterDecrementClass;

        MethodExp PerCounterIncrementProperties;
        PropertyExp PerCounterDecrementProperties;

        public AutoMapperCodeDomGenerator(CSharpParser.OutputWriter writer, string name)
        {

            AutoMapperName = name;

            VariableAutomapper = new VariableExp(AutoMapperName, TypeExp.Object);

            PerCounterClass = VariableAutomapper.Property("counterClass", TypeExp.Object);

            PerCounterIncrementClass = PerCounterClass.Method("Increment").Call();
            PerCounterDecrementClass = PerCounterClass.Method("Decrement").Call();

            PerCounterProperties = VariableAutomapper.Property("counterProperties", TypeExp.Object);
            PerCounterIncrementProperties = PerCounterProperties.Method("IncrementBy");

            PerCounterDecrementProperties = PerCounterProperties.Property("RawValue", Helper.Long);

            this._writer = writer;

        }

 

        private int _cnt = 0;
        internal CodeCompileUnit Generate(CodeCompileUnit code, MappingModel model)
        {

            _cnt = 0;
            using (new RegionExp(code, "Mappers"))
            {

                string n = model.Namespace;
                NamespaceExp elementNamespace = new NamespaceExp(n);
                code.Namespaces.Add(elementNamespace);

                TypeMember Class = null;

                // ------------ counter perf ------------

                if (model.Instrumentation)
                {
                    Class = Helper.CreateClass(AutoMapperName, true, false);
                    Class.IsPartial = true;                    
                    elementNamespace.Types.Add(Class);

                    CreatePerfCounters(elementNamespace, Class, model);

                }

                // ------------ counter perf ------------



                // --------------- Indexes --------------
           
                List<String> ls = GetIndexes(model).Distinct().ToList();
                if (ls.Count > 0)
                {
                    if (Class == null)
                    {
                        Class = Helper.CreateClass(AutoMapperName, true, false);
                        Class.IsPartial = true;
                        elementNamespace.Types.Add(Class);
                    }
                    System.Text.RegularExpressions.Regex regEx = new Regex(@"[^\w]");


                    var MyCleanUp = Class.Members.CreateMethod("Clear", MemberVisibility.Public, false);

                    foreach (string item in ls)
                    {

                        string[] tb = item.Split('§');

                        TypeExpression tname1 = (TypeExpression)tb[0];
                        TypeExpression tname2 = (TypeExpression)tb[1];
                        

                        TypeExp t = new TypeExp(typeof(Hashtable));
                        string name = regEx.Replace(tname1.FullName + "_" + tname2.FullName, "");
                        var v = new VariableExp(name + "Dic" + Guid.NewGuid().ToString().Replace("-", "").Substring(0, 5), t);
                        IndexBag b = new IndexBag() { Variable = v };
                        IndexexDic.Add(item, b);

                        var myMember = Class.Members.CreateFieldAutoInit(v.VariableName, MemberVisibility.Public, false, t);
                        
                        VariableExp v1 = new VariableExp(myMember.Name, t);
                        MyCleanUp.Statements.Add(v1.Method("Clear").Call());

                    }
                }

                // --------------- Indexes -------------- 



                // -------------- Mappeurs -------------- 

                foreach (Mapper mapper in model.Mappers)
                    if (mapper.MappingSource != null && mapper.MappingDestination != null)
                    {
                        
                        _writer.WriteLine(string.Format("Generate mapper {0}.{1} to {2}.{3}", mapper.MappingSource.TypeElement.Namespace, mapper.MappingSource.TypeElement.Name, mapper.MappingDestination.TypeElement.Namespace, mapper.MappingDestination.TypeElement.Name));

                        if (mapper.Generate)
                        {
                            try
                            {
                                GenerateMapper(elementNamespace, mapper, model);
                            }
                            catch (Exception e)
                            {

                                StackTrace trace = new StackTrace(e, true);

                                throw;

                            }
                        }

                    }

                // -------------- Mappeurs -------------- 


                // insere les namespaces
                var p = elementNamespace.GetNamespaces(3, n);
                foreach (CodeNamespaceImport item in p)
                    elementNamespace.Imports.Add(item);

            }

            
            return code;
        }

        private IEnumerable<string> GetIndexes(MappingModel model)
        {

            foreach (var m in model.Mappers)
            {

                if (m.ReferenceMultiple)
                    yield return m.MappingSource.TypeElement.AssemblyQualifiedName + "§" + m.MappingDestination.TypeElement.AssemblyQualifiedName;

                foreach (var p in m.ItemMaps)
                    if (p.IsRecursif)
                        yield return p.SourceType + "§" + p.TargetType;

            }

        }

        private IEnumerable<ItemMap> GetItemMap(MappingModel model, string assemblyQualifiedName)
        {

            foreach (var m in model.Mappers)
            {

                foreach (var p in m.ItemMaps)
                    if (p.IsRecursif)
                        yield return p;

            }

        }



        #region Perf counters

        private void CreatePerfCounters(NamespaceExp elementNamespace, TypeMember Class, MappingModel model)
        {

            var categ = Helper.Primitive(model.Category);
            
            var t = new TypeExp(typeof(PerformanceCounter));


            var counterClass = Class.Members.CreateFielWithInit
                (
                    "counterClass",
                    MemberVisibility.InternalProtected,
                    false,
                    t,
                    VariableAutomapper.Method("InitializeClass", t).Call()
                ).Static();

            var counterProperties = Class.Members.CreateFielWithInit
                (
                    "counterProperties",
                    MemberVisibility.InternalProtected,
                    false,
                    t,
                    VariableAutomapper.Method("InitializeProperties", t).Call()
                ).Static();


            VariableExp performanceCounterCategory = new VariableExp("PerformanceCounterCategory", TypeExp.Object);



            var methodProperties = Class.Members.CreateMethod("InitializeClass", MemberVisibility.InternalProtected, false).Static();
            methodProperties.ReturnType = t;
            var p1 = Helper.Primitive("Created class");
            var a1 = performanceCounterCategory.Method("Exists", Helper.Bool).Call(categ);
            var b1 = performanceCounterCategory.Method("CounterExists", Helper.Bool).Call(p1, categ);
            var Try1 = methodProperties.Statements.Try();
            Try1.CatchClauses.Catch("e", new TypeExp(typeof(Exception)));
            var If1 = Try1.TryStatements.If(a1);
            var If12 = If1.TrueStatements.If(b1);            
            If12.TrueStatements.Return(counterProperties.New(categ, p1, Helper.False));
            methodProperties.Statements.Return(Helper.Null);


            var methodClass = Class.Members.CreateMethod("InitializeProperties", MemberVisibility.InternalProtected, false).Static();
            methodClass.ReturnType = t;
            var p2 = Helper.Primitive("Created properties");
            var a2 = performanceCounterCategory.Method("Exists", Helper.Bool).Call(categ);
            var b2 = performanceCounterCategory.Method("CounterExists", Helper.Bool).Call(p2, categ);
            var Try2 = methodClass.Statements.Try();
            Try2.CatchClauses.Catch("e", new TypeExp(typeof(Exception)));
            var If21 = Try2.TryStatements.If(a2);
            var If22 = If21.TrueStatements.If(b2);
            If22.TrueStatements.Return(counterProperties.New(categ, p2, Helper.False));
            methodClass.Statements.Return(Helper.Null);

            
        }


        #endregion


        /// <summary>
        /// Generates the mapper.
        /// </summary>
        /// <param name="code">The code.</param>
        /// <param name="mapper">The mapper.</param>
        /// <param name="model">The model.</param>
        internal void GenerateMapper(CodeNamespace code, Mapper mapper, MappingModel model)
        {

            Stack<RecurcifBag> root = new Stack<RecurcifBag>();            

            VariableExp Source = new VariableExp("source", mapper.MappingSource.TypeElement.AssemblyQualifiedName);
            VariableExp Target = new VariableExp("target", mapper.MappingDestination.TypeElement.AssemblyQualifiedName);

            if (mapper.MappingSource.TypeElement == null || mapper.MappingDestination.TypeElement == null)
                return;

            Dictionary<string, TypeExp> ListeConverters = new Dictionary<string, TypeExp>();

            CodeTypeDeclaration Class = CreateClass(mapper);
            code.Types.Add(Class);
            Class.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format("<summary>Mapping's functions for {0} to {1} </summary>", Source.Type.TypeExpression.Name, Target.Type.TypeExpression.Name), true)));
                        
            var m1 = MethodMap1Parameter(Class, mapper, ListeConverters, model, Source, Target, root);
            m1.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format("<summary>Map all the properties from {0} in a new instance of {1}</summary>", Source.VariableName, Target.Type.TypeExpression.Name), true)));
            m1.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The {0}. If the source is null, the method throw a exception.</param>", Source.VariableName), true)));            

            var m2 = MethodMap2Parameter(Class, mapper, ListeConverters, model, Source, Target, root);
            m2.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format("<summary>Map all the properties from {0} in target</summary>", Source.VariableName), true)));
            m2.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The {0}. If the source is null, the method throw a exception.</param>", Source.VariableName), true)));
            m2.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The value is created if null.</param>", Target.VariableName), true)));

            CreatefieldsConverter(Class, ListeConverters);

            // Enumerables
            TypeExpression typeRetour = TypeExpression.Analyse(typeof(ICollection<string>));
            typeRetour.SubType.TypeName = Target.Type.TypeExpression.AssemblyQualifiedName;
            VariableExp TargetEnumerable = new VariableExp(Target.VariableName, typeRetour);

            TypeExpression typeSource = TypeExpression.Analyse(typeof(IEnumerable<string>));
            typeSource.SubType.TypeName = Source.Type.TypeExpression.AssemblyQualifiedName;
            VariableExp SourceEnumerable = new VariableExp(Source.VariableName, typeSource);

            var m3 = MethodMap1ParameterIEnumerable(Class, mapper, ListeConverters, model, SourceEnumerable, TargetEnumerable, root);
            m3.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format("<summary>Map all the item from the source list in a new instance of {0}</summary>", Target.Type.TypeExpression.Name), true)));
            m3.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The {0}.If the source is null, the method throw a exception.</param>", Source.VariableName), true)));
            
            var m4 = MethodMap2ParameterIEnumerable(Class, mapper, ListeConverters, model, SourceEnumerable, TargetEnumerable, root);
            m4.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format("<summary>Map all the properties from {0} in {1} List</summary>", Source.VariableName, Target.Type.TypeExpression.Name), true)));
            m4.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The {0}. If the source is null, the method throw a exception.</param>", Source.VariableName), true)));
            m4.Comments.Add(new CodeCommentStatement(new CodeComment(string.Format(@"<param name=""{0}"">The value is created if null.</param>", Target.VariableName), true)));
        }

        private void CreatefieldsConverter(CodeTypeDeclaration Class, Dictionary<string, TypeExp> ListeConverters)
        {            
            foreach (var item in ListeConverters)
            {
                string n = "_C" + item.Value.TypeExpression.Name.Replace(".", "");

                Class.Members.CreateFieldAutoInit(n, MemberVisibility.Private, false, item.Value).Static();

            }
        }



        #region Methodes

        private MethodMember MethodMapNoParameter(Mapper mapper, VariableExp Source, VariableExp Target, bool debuggerStepThrough, bool returnTargetIfNull, bool isAbstract)
        {

            //Type t1 = Source.Type.TypeExpression.Evaluate(FindType);

            _cnt = 0;


            ParameterExp p1 = null;
            //if (t1 != null)
                //p1 = new ParameterExp(t1, Source.VariableName);
            //else
                p1 = new ParameterExp(Source.Type.TypeExpression.AssemblyQualifiedName, Source.VariableName);

            MethodMember method = Helper.CreateMethod(
                mapper.MapMethodName, MemberVisibility.Public, 
                true,
                Target.Type,
                p1
                );


            if (!mapper.MappingSource.TypeElement.IsValueType || isAbstract)
            {
                if (!returnTargetIfNull)
                    method.Statements.If(Source.IsNull, mapper.MappingSource.TypeElement.IsValueType || isAbstract ? Helper.ReturnNull() : new CodeMethodReturnStatement(Target.New()));
                else
                {
                    var blockIf = method.Statements.If(Source.IsNull);
                    blockIf.TrueStatements.Return(Target);
                }
            }

            if (debuggerStepThrough)            
                method.CreateAttribute(typeof(System.Diagnostics.DebuggerStepThroughAttribute));
                       
            return method;
        }

        private CodeMemberMethod MethodMap1Parameter(CodeTypeDeclaration Class, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, MappingModel model, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {

            var t = mapper.MappingDestination.TypeElement;
            var a = t.IsAbstract || t.IsInterface;

            _cnt = 0;
            MethodMember method = MethodMapNoParameter(mapper, Source, Target, model.DebuggerStepThrough, false, a);

            if (mapper.MappingDestination.TypeElement.IsValueType)
                method.Statements.Declare(Target);
            else
                method.Statements.Declare(Target, Helper.Null);


            Class.Members.Add(method);

            CodeExpression expr = CreateTarget(mapper, Source, Target);
            string nameType = Source.Type.TypeExpression.AssemblyQualifiedName + "§" + Target.Type.TypeExpression.AssemblyQualifiedName;
            if (IndexexDic.ContainsKey(nameType))
            {
                IndexBag index = IndexexDic[nameType];
                CodeExpression Source2 = null;
                if (mapper.IndexProperty == "Instance")
                    Source2 = Source;
                else
                    Source2 = Source.Property(mapper.IndexProperty, typeof(object));

                var If = method.Statements.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(Source2));
                var s = Helper.Cast(index.Variable.Index(Source2), Target.Type);
                If.TrueStatements.Return(s);
                If.FalseStatements.Set(Target, expr);
                If.FalseStatements.Add(index.Variable.Method("Add").Call(Source2, Target));                
            }
            else
            {

                if (!mapper.MappingDestination.TypeElement.IsValueType)
                    method.Statements.Set(Target, expr);

                if (mapper.MappingModel.Instrumentation)
                {
                    var If3 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                    If3.TrueStatements.Add(PerCounterIncrementClass);
                }
            }

            CreateMap(method.Statements, mapper, ListeConverters, model, Source, Target, queue);

            if (mapper.MappingModel.Instrumentation)
            {
                var If4 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                If4.TrueStatements.Add(PerCounterDecrementClass);               
            }
            

            method.Statements.Return(Target);
            return method;
        }

        private CodeMemberMethod MethodMap2Parameter(CodeTypeDeclaration Class, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, MappingModel model, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {

            _cnt = 0;

            var t = mapper.MappingDestination.TypeElement;
            var a = t.IsAbstract || t.IsInterface;

            CodeMemberMethod method = MethodMapNoParameter(mapper, Source, Target, model.DebuggerStepThrough, true, a);
            method.Parameters.Add((ParameterExp)Target);
            Class.Members.Add(method);

            CodeExpression expr = CreateTarget(mapper, Source, Target);
            CodeStatementCollection Cde = null;


            if (!mapper.MappingDestination.TypeElement.IsValueType)
            {
                var If = method.Statements.If(Helper.IsNull(Target));
                Cde = If.TrueStatements;
            }
            else            
                Cde = method.Statements;


            string nameType = Source.Type.TypeExpression.AssemblyQualifiedName + "§" + Target.Type.TypeExpression.AssemblyQualifiedName;
            if (IndexexDic.ContainsKey(nameType))
            {
                IndexBag index = IndexexDic[nameType];

                CodeExpression Source2 = null;
                if (mapper.IndexProperty == "Instance")
                    Source2 = Source;
                else
                    Source2 = Source.Property(mapper.IndexProperty, typeof(object));

                var If1 = Cde.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(Source2));
                var s = Helper.Cast(index.Variable.Index(Source2), Target.Type);
                If1.TrueStatements.Return(s);
                If1.FalseStatements.Set(Target, expr);
                If1.FalseStatements.Add(index.Variable.Method("Add").Call(Source2, Target));                
            }
            else
            {                
                if (model.Instrumentation)
                {
                    var If2 = Cde.If(Helper.IsNotNull(PerCounterClass));
                    If2.TrueStatements.Add(PerCounterIncrementClass);
                }

                Cde.Set(Target, expr);

            }

            CreateMap(method.Statements, mapper, ListeConverters, model, Source, Target, queue);

            if (model.Instrumentation)
            {
                var If2 = Cde.If(Helper.IsNotNull(PerCounterClass));
                If2.TrueStatements.Add(PerCounterDecrementClass);
            }
            

            method.Statements.Return(Target);
            return method;
        }

        private CodeMemberMethod MethodMap1ParameterIEnumerable(CodeTypeDeclaration Class, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, MappingModel model, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {

            _cnt = 0;
            MethodMember method = MethodMapNoParameter(mapper, Source, Target, model.DebuggerStepThrough, false, true);

            Class.Members.Add(method);

            var expr = CreateEnumerable(mapper, Target);
            method.Statements.Declare(Target, expr);

            if (mapper.MappingModel.Instrumentation)
            {
                var If2 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                If2.TrueStatements.Add(PerCounterIncrementClass);
            }

            VariableExp current = null;

            TypeExpression myTypeEnum = GetSubType(Source.Type.TypeExpression, mapper.MappingSource.TypeElement.ItemType);

            //foreach (T current in source.PropertyName)
            var f2 = method.Statements.ForEach(myTypeEnum, Source, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());
            var f1 = f2.Statements.If(Helper.IsNotNull(current));


            VariableExp targetItem = new VariableExp("targetItem" + _cnt.ToString(), Target.Type.TypeExpression.SubType);

            var expr2 = CreateTarget(mapper, current, targetItem);
            string nameType = Source.Type.TypeExpression.AssemblyQualifiedName + "§" + Target.Type.TypeExpression.AssemblyQualifiedName;
            if (IndexexDic.ContainsKey(nameType))
            {
                IndexBag index = IndexexDic[nameType];

                CodeExpression Source2 = null;
                if (mapper.IndexProperty == "Instance")
                    Source2 = Source;
                else
                    Source2 = Source.Property(mapper.IndexProperty, typeof(object));

                var If = f1.TrueStatements.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(Source2));
                var s = Helper.Cast(index.Variable.Index(Source2), Target.Type);
                If.TrueStatements.Return(s);
                If.FalseStatements.Set(Target, expr);
                If.FalseStatements.Add(index.Variable.Method("Add").Call(Source2, Target));                
            }
            else
            {
                f1.TrueStatements.Declare(targetItem, expr2);
                if (model.Instrumentation)
                {
                    var If3 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                    If3.TrueStatements.Add(PerCounterIncrementClass);
                }
            }

            CreateMap(f1.TrueStatements, mapper, ListeConverters, model, current, targetItem, queue);

            if (model.Instrumentation)
            {
                var If6 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                If6.TrueStatements.Add(PerCounterDecrementClass);               
            }
            f1.TrueStatements.Add(Target.Method("Add").Call(targetItem));

            if (mapper.MappingModel.Instrumentation)
            {
                var If7 = f1.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If7.TrueStatements.Add(PerCounterDecrementClass);               
            }

            method.Statements.Return(Target);
            return method;
        }

        private CodeMemberMethod MethodMap2ParameterIEnumerable(CodeTypeDeclaration Class, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, MappingModel model, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {
            _cnt = 0;

            //Type t1 = Target.Type.TypeExpression.Evaluate(FindType);

            CodeMemberMethod method = MethodMapNoParameter(mapper, Source, Target, model.DebuggerStepThrough, false, true);
            method.Parameters.Add(new ParameterExp(Target.Type.TypeExpression.AssemblyQualifiedName, Target.VariableName));

            Class.Members.Add(method);            

            var m = CreateEnumerable(mapper, Target);
            var If = method.Statements.If(Helper.IsNull(Target), Helper.Set(Target, m));

            if (mapper.MappingModel.Instrumentation)
            {
                var If3 = If.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If3.TrueStatements.Add(PerCounterIncrementClass);
            }

            VariableExp current = null;

            //foreach (T current in source.PropertyName)

            TypeExpression myTypeEnum = GetSubType(Source.Type.TypeExpression, mapper.MappingSource.TypeElement.ItemType);

            var f2 = method.Statements.ForEach(myTypeEnum, Source, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());
            var f1 = f2.Statements.If(Helper.IsNotNull(current));

            VariableExp targetItem = new VariableExp("targetItem" + _cnt.ToString(), Target.Type.TypeExpression.SubType);
            var expr2 = CreateTarget(mapper, current, targetItem);
            string nameType = Source.Type.TypeExpression.AssemblyQualifiedName + "§" + Target.Type.TypeExpression.AssemblyQualifiedName;
            if (IndexexDic.ContainsKey(nameType))
            {
                IndexBag index = IndexexDic[nameType];

                CodeExpression Source2 = null;
                if (mapper.IndexProperty == "Instance")
                    Source2 = Source;
                else
                    Source2 = Source.Property(mapper.IndexProperty, typeof(object));

                var If6 = f1.TrueStatements.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(Source2));
                var s = Helper.Cast(index.Variable.Index(Source2), Target.Type);
                If6.TrueStatements.Return(s);
                If6.FalseStatements.Set(Target, expr2);
                If6.FalseStatements.Add(index.Variable.Method("Add").Call(Source2, Target));                
            }
            else
            {
                f1.TrueStatements.Declare(targetItem, expr2);
                if (model.Instrumentation)
                {
                    var If3 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                    If3.TrueStatements.Add(PerCounterIncrementClass);
                }
            }

            CreateMap(f1.TrueStatements, mapper, ListeConverters, model, current, targetItem, queue);            

            f1.TrueStatements.Add(Target.Method("Add").Call(targetItem));

            if (model.Instrumentation)
            {
                var If4 = method.Statements.If(Helper.IsNotNull(PerCounterClass));
                If4.TrueStatements.Add(PerCounterDecrementClass);
            }

            method.Statements.Return(Target);
            return method;
        }

        #endregion

        /// <summary>
        /// entree des conversions
        /// </summary>
        /// <param name="method"></param>
        /// <param name="mapper"></param>
        /// <param name="ListeConverters"></param>
        /// <param name="model"></param>
        private void CreateMap(CodeStatementCollection method, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, MappingModel model, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {

            //VariableExp automapper = new VariableExp(AutoMapperName, TypeExp.Object);

            method.Add(new CodeCommentStatement(new CodeComment(string.Format(" ---------------- Copy {0} ({1})------------------------", mapper.Name, Source.Type.TypeExpression.Name))));

            queue.Push(new RecurcifBag() { Mapper = mapper, Source = Source, Count = 0 });

            _cnt++;

            if (mapper.ExecuteMethodBeforeCast)
            {

                CodeExpression cc = Source.root ?? Source;
                method.Add(MyThis.Method("ExecuteBeforeMapping", TypeExp.Void).Call(cc, Target));

            }

            int cntprop = mapper.ItemMaps.Where(c => c.Enable).Count();


            if (model.Instrumentation)
            {
                var If2 = method.If(Helper.IsNotNull(PerCounterProperties));
                If2.TrueStatements.Add(PerCounterIncrementProperties.Call(Helper.Primitive(cntprop)));
            }

            CreateMapProperties(method, mapper, ListeConverters, model, Source, Target, queue);

            // AutoMapper.counterProperties.RawValue = AutoMapper.counterProperties.RawValue - cntprop;
            if (model.Instrumentation)
            {
                var If3 = method.If(Helper.IsNotNull(PerCounterProperties));
                If3.TrueStatements.Set(PerCounterDecrementProperties, Helper.Subtract(PerCounterDecrementProperties, Helper.Primitive(cntprop)));      
            }


            if (mapper.MappingSource.TypeElement.IsEnumerable)
            {
                CreateEnumerator(method, mapper, model, Source, Target);

            }



            if (mapper.ExecuteMethodAfterCast)
            {

                CodeExpression cc = Source.root ?? Source;
                method.Add(MyThis.Method("ExecuteAfterMapping", TypeExp.Void).Call(cc, Target));

            }
            method.Add(new CodeCommentStatement(new CodeComment(string.Format(" ---------------- End Copy {0} ({1})--------------------", mapper.Name, Source.Type.TypeExpression.Name))));

            // Compteurs
            RecurcifBag bag = queue.Pop();

        }

        private void CreateMapProperties(CodeStatementCollection method, Mapper mapper, Dictionary<string, TypeExp> ListeConverters, MappingModel model, VariableExp Source, VariableExp Target, Stack<RecurcifBag> queue)
        {


            Type SourceType = Source.Type.TypeExpression.Evaluate(FindType);
            Type DestinationType = Target.Type.TypeExpression.Evaluate(FindType);


            // valeur simples comme enum, int, long, string Ect ...
            if (IsSimpleValue(SourceType) && IsSimpleValue(DestinationType))
            {
                if (SourceType == DestinationType)
                {
                    method.Set(Target, Source);
                }
                else
                {
                    // voir pour le cast
                    //method.Set(Target, );
                }

                return;

            }


            #region map properties

            foreach (ItemMap item in mapper.ItemMaps)
            {

                PropertyExp SourceProperty = Source.Property(item.SourceName, item.SourceType);
                PropertyExp TargetProperty = Target.Property(item.TargetName, item.TargetType);

                if (item.Enable)
                {


                    CodeStatementCollection method2 = null;
                    if (UseContitionnalMap(item) && !item.SourceName.Contains("."))
                    {
                        var If1 = method.If(new MethodExp(Helper.This(), item.ConditionnalMappingMethod.Trim()).Call(Source));
                        method2 = If1.TrueStatements;
                    }
                    else
                        method2 = method;


                    switch (item.TypeCast)
                    {

                        case CastType.ObservableCollection:
                        case CastType.List:
                        case CastType.Collection:
                        case CastType.Hashset:
                            CreateList(method2, item, Source, Target, SourceProperty, TargetProperty, ListeConverters, model, queue);
                            break;

                        case CastType.Explicit:
                            CreateExplicitProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            break;

                        case CastType.Custom:
                            CreateCustomMapProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            break;

                        case CastType.Map:
                            CreateConverterMapClass(method2, item, model, Source, Target, SourceProperty, TargetProperty, ListeConverters, queue);
                            break;

                        case CastType.Implicit:
                        case CastType.None:
                            CreateSimpleCopy(method2, SourceProperty, TargetProperty, item);
                            break;

                        case CastType.EnumByValue:
                            CreateEnumByValue(method2, SourceProperty, TargetProperty, item);
                            break;

                        case CastType.EnumByLabel:
                            CreateEnumByLabel(method2, SourceProperty, TargetProperty, item);

                            break;

                        case CastType.TypeConverter:
                            TypeExp c = CreateTypeConverterMapProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            if (!ListeConverters.ContainsKey(c.TypeExpression.FullName))
                                ListeConverters.Add(c.TypeExpression.FullName, c);
                            break;

                        case CastType.IValueConverter:
                            TypeExp d = CreateIValueConverterMapProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            if (!ListeConverters.ContainsKey(d.TypeExpression.FullName))
                                ListeConverters.Add(d.TypeExpression.FullName, d);
                            break;

                        case CastType.IConverter:
                            TypeExp c1 = CreateIConverterMapProperty(method2, item, Source, Target, SourceProperty, TargetProperty);
                            if (!ListeConverters.ContainsKey(c1.TypeExpression.FullName))
                                ListeConverters.Add(c1.TypeExpression.FullName, c1);
                            break;

                    }

                }
            }

            #endregion
        }

        private static bool UseContitionnalMap(ItemMap item)
        {

            if (!string.IsNullOrEmpty(item.ConditionnalMappingMethod.Trim()))
                switch (item.TypeCast)
                {
                    case CastType.ObservableCollection:
                    case CastType.List:
                    case CastType.Collection:
                    case CastType.Hashset:
                        return false;
                    default:
                        return true;
                }
           
            return false;

        }

        private static void CreateSimpleCopy(CodeStatementCollection methode, PropertyExp SourceProperty, PropertyExp TargetProperty, ItemMap map)
        {


            CodeStatementCollection method = methode;

            if (map.SourceIsClass && !(SourceProperty.Type.TypeExpression.FullName == typeof(string).FullName))
            {
                var if1 = method.If(Helper.IsNotNull(SourceProperty));
                method = if1.TrueStatements;
            }
            else if (SourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source = SourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = SourceProperty.PropertyName.Split('.');
                for (int i = 0; i < tb.Length - 1; i++)
                {

                    source = new PropertyExp(source, tb[i], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source));
                    method = if2.TrueStatements;

                }

            }


            System.CodeDom.CodeExpression s = null;

            if (map.SourceIsNullable && !map.TargetIsNullable)
                s = SourceProperty.Method("GetValueOrDefault").Call() as System.CodeDom.CodeExpression;

            // cas particuliers
            else if (!TryGetSpecificCast(SourceProperty, TargetProperty, map, out s))
                s = SourceProperty;

            method.Set(TargetProperty, s);

        }



        private static void CreateEnumByLabel(CodeStatementCollection methode, PropertyExp SourceProperty, PropertyExp TargetProperty, ItemMap map)
        {            

            CodeStatementCollection method = methode;

            if (SourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source = SourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = SourceProperty.PropertyName.Split('.');
                for (int i = 0; i < tb.Length - 1; i++)
                {
                    source = new PropertyExp(source, tb[i], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source));
                    method = if2.TrueStatements;
                }

            }


            CodeExpression _source = SourceProperty;
            if (map.SourceIsEnum)
                _source = SourceProperty.Method("ToString").Call();

            var meth = Helper.Cast(new MethodExp(new CodeVariableReferenceExpression("System.Enum"), "Parse").Call(TargetProperty.Type.TypeOf(), _source), TargetProperty.Type);

            method.Set(TargetProperty, meth);

        }

        private static void CreateEnumByValue(CodeStatementCollection methode, PropertyExp SourceProperty, PropertyExp TargetProperty, ItemMap map)
        {

            CodeStatementCollection method = methode;

            if (SourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source = SourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = SourceProperty.PropertyName.Split('.');
                for (int i = 0; i < tb.Length - 1; i++)
                {
                    source = new PropertyExp(source, tb[i], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source));
                    method = if2.TrueStatements;
                }

            }


            method.Set(TargetProperty, SourceProperty);

        }

        private static bool TryGetSpecificCast(PropertyExp SourceProperty, PropertyExp TargetProperty, ItemMap map, out CodeExpression t)
        {

            string source = SourceProperty.Type.TypeExpression.FullName;
            string target = TargetProperty.Type.TypeExpression.FullName;

            t = null;

            if (source == typeof(Guid).FullName && target == typeof(string).FullName)
            {
                t = SourceProperty.Method("ToString").Call();
                return true;
            }

            else if (source == typeof(Guid).FullName && target == typeof(byte[]).FullName)
            {
                t = SourceProperty.Method("ToByteArray").Call();
                return true;
            }

            else if ((source == typeof(string).FullName || source == typeof(byte[]).FullName) && target == typeof(Guid).FullName)
            {
                TypeExp r = new TypeExp(typeof(Guid));
                t = r.New(SourceProperty);
                return true;
            }

            else if (source == typeof(byte[]).FullName && target == typeof(string).FullName)
            {
                VariableExp v1 = new VariableExp("Convert", typeof(Convert));
                t = v1.Method("ToBase64String").Call(SourceProperty);
                return true;
            }
            else if (source == typeof(string).FullName && target == typeof(byte[]).FullName)
            {
                VariableExp v1 = new VariableExp("Convert", typeof(Convert));
                t = v1.Method("FromBase64String").Call(SourceProperty);
                return true;
            }
            else if (source == typeof(string).FullName && (target == typeof(short).FullName || target == typeof(Int16).FullName))
            {
                VariableExp v1 = new VariableExp("Convert", typeof(Convert));
                t = v1.Method("ToInt16").Call(SourceProperty);
                return true;
            }
            else if (source == typeof(string).FullName && (target == typeof(int).FullName || target == typeof(Int32).FullName))
            {
                VariableExp v1 = new VariableExp("Convert", typeof(Convert));
                t = v1.Method("ToInt32").Call(SourceProperty);
                return true;
            }
            else if (source == typeof(string).FullName && (target == typeof(long).FullName || target == typeof(Int64).FullName))
            {
                VariableExp v1 = new VariableExp("Convert", typeof(Convert));
                t = v1.Method("ToInt64").Call(SourceProperty);
                return true;
            }
            else if (source == typeof(string).FullName && target == typeof(UInt64).FullName)
            {
                VariableExp v1 = new VariableExp("Convert", typeof(Convert));
                t = v1.Method("ToUInt64").Call(SourceProperty);
                return true;
            }
            else if (source == typeof(string).FullName && target == typeof(UInt32).FullName)
            {
                VariableExp v1 = new VariableExp("Convert", typeof(Convert));
                t = v1.Method("ToUInt32").Call(SourceProperty);
                return true;
            }
            else if (source == typeof(string).FullName && target == typeof(UInt16).FullName)
            {
                VariableExp v1 = new VariableExp("Convert", typeof(Convert));
                t = v1.Method("ToUInt16").Call(SourceProperty);
                return true;
            }


            else if (target == "System.String")
            {
                if (map.SourceIsEnum)
                {
                    t = SourceProperty.Method("ToString").Call();
                    return true;
                }
                else if (map.SourceIsNullable)
                {
                    t = SourceProperty.Method("GetValueOrDefault").Call().Method("ToString").Call();
                    return true;
                }
                
            }

            return false;
        }

        /// <summary>
        /// entree des conversions
        /// </summary>
        /// <param name="method"></param>
        /// <param name="mapper"></param>
        /// <param name="ListeConverters"></param>
        /// <param name="model"></param>
        private void CreateMapReflexion(CodeStatementCollection method, Dictionary<string, TypeExp> ListeConverters, MappingModel model, VariableExp Source, VariableExp Target, Queue<RecurcifBag> queue, Type SourceType, Type DestinationType)
        {

        }

        private void CreateList(CodeStatementCollection method, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, MappingModel model, Stack<RecurcifBag> queue)
        {

            method.Add(new CodeCommentStatement(new CodeComment(string.Format(" ---------------- Copy Liste {0} ------------------------", sourceProperty.Type.TypeExpression.ToString(false)))));

            TypeExpression t = null;

            if (item.TypeCast == CastType.Collection)
                t = targetProperty.Type.TypeExpression.Translate(typeof(Collection<>));

            else if (item.TypeCast == CastType.List)
                t = targetProperty.Type.TypeExpression.Translate(typeof(System.Collections.Generic.List<>));

            else if (item.TypeCast == CastType.ObservableCollection)
                t = targetProperty.Type.TypeExpression.Translate(typeof(ObservableCollection<>));

            else if (item.TypeCast == CastType.Hashset)
                t = targetProperty.Type.TypeExpression.Translate(typeof(HashSet<>));

            VariableExp liste = new VariableExp("liste", t);

            // cherche le type enuméré
            string ss1 = GetSubTypeString(sourceProperty.Type.TypeExpression, item.SourceItemType);

            string tt1 = GetSubTypeString(targetProperty.Type.TypeExpression, item.TargetItemType);
          
            

            // cherche un mapper qui corresponde aux type enumérés.
            Mapper mapper2 = item.Mapper.MappingModel.FindMapperFor
                (
                    ss1,
                    tt1
                );

            Type TypeSource;
            Type TypeDestination;

            if (mapper2 != null)
                CopyListeMapper(method, item, target, sourceProperty, targetProperty, ListeConverters, model, queue, liste, mapper2);


            // on essaie les types de bases
            else 
            {
                
                TypeSource = TypeExpression.Analyse(ss1).Evaluate(FindType);
                TypeDestination = TypeExpression.Analyse(tt1).Evaluate(FindType);

                if (null != TypeSource && null != TypeDestination)            
                    CopyListeReflexion(method, item, target, sourceProperty, targetProperty, ListeConverters, model, queue, liste, TypeSource, TypeDestination);

                else
                    method.Add(new CodeCommentStatement(new CodeComment("Cannot find the good mapper")));

            }
            


            method.Add(new CodeCommentStatement(new CodeComment(string.Format(" ---------------- End Copy Liste {0} ---------------------", sourceProperty.Type.TypeExpression.ToString(false)))));
        }


        public string GetSubTypeString(TypeExpression type, string item)
        {

            var t1 = type.SubType;

            string tt1 = string.Empty;

            if (t1 == null)
                tt1 = item;
            else
                tt1 = t1.AssemblyQualifiedName;

            return tt1;

        }


        public TypeExpression GetSubType(TypeExpression type, string item)
        {
            TypeExpression t1 = type.SubType;

            if (t1 == null)
                t1 = TypeExpression.Analyse(item);

            return t1;

        }


        private void CopyListeMapper(CodeStatementCollection method, ItemMap item, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, MappingModel model, Stack<RecurcifBag> queue, VariableExp liste, Mapper mapper2)
        {
            // Process Mapper

            var methode = method.If(Helper.IsNotNull(sourceProperty));


            //VariableExp mapper2V = new VariableExp(mapper2.Name, "");
            // target.PropertyName = new List<T>(); 
            if (liste.Type.Equals(targetProperty.Type))
                methode.TrueStatements.Set(targetProperty, liste.New());
            else
            {
                methode.TrueStatements.Set(targetProperty, new CodeCastExpression(targetProperty.Type, liste.New()));
            }

            if (model.Instrumentation)
            {
                var If3 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If3.TrueStatements.Add(PerCounterIncrementClass);
            }

            VariableExp current = null;

            TypeExpression myTypeEnum = GetSubType(sourceProperty.Type.TypeExpression, item.SourceItemType);


            //foreach (T current in source.PropertyName)
            var f2 = methode.TrueStatements.ForEach(myTypeEnum, sourceProperty, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());
            var f11 = f2.Statements.If(Helper.IsNotNull(current));


            // copy conditionnelle
            CodeConditionStatement f1 = null;
            if (!string.IsNullOrEmpty(item.ConditionnalMappingMethod.Trim()))        
                f1 = f11.TrueStatements.If(new MethodExp(Helper.This(), item.ConditionnalMappingMethod.Trim()).Call(current));            
            else
                f1 = f11;


            VariableExp target2 = f1.TrueStatements.Declare(target.VariableName + _cnt.ToString(), GetSubType(targetProperty.Type.TypeExpression, item.TargetItemType));
            bool created = false;
            CodeExpression expr = CreateTarget(mapper2, current, target2);
            string nameType = current.Type.TypeExpression.AssemblyQualifiedName + "§" + target2.Type.TypeExpression.AssemblyQualifiedName;
            if (IndexexDic.ContainsKey(nameType))
            {
                IndexBag index = IndexexDic[nameType];
                var If = f1.TrueStatements.If(index.Variable.Method("ContainsKey", Helper.Bool).Call(current));
                var s = Helper.Cast(index.Variable.Index(current), target2.Type);
                If.TrueStatements.Set(target2, s);
                
                If.FalseStatements.Set(target2, expr);
                If.FalseStatements.Add(index.Variable.Method("Add").Call(current, target2));

                CreateMap(If.FalseStatements, mapper2, ListeConverters, model, current, target2, queue);

            }
            else
            {
                f1.TrueStatements.Set(target2, expr);
                if (model.Instrumentation)
                {
                    var If4 = f1.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                    If4.TrueStatements.Add(PerCounterIncrementClass);
                }

                CreateMap(f1.TrueStatements, mapper2, ListeConverters, model, current, target2, queue);
                created = true;

            }


            if (targetProperty.Type.TypeExpression.Equals(liste.Type.TypeExpression))
                f1.TrueStatements.Add(targetProperty.Method("Add").Call(target2));
            else
                f1.TrueStatements.Add(targetProperty.As(liste.Type).Method("Add", liste.Type).Call(target2));


            if (model.Instrumentation && created)
            {

                var If5 = methode.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If5.TrueStatements.Add(PerCounterDecrementClass);

                var If6 = f1.TrueStatements.If(Helper.IsNotNull(PerCounterClass));
                If6.TrueStatements.Add(PerCounterDecrementClass);

            }
        }


        private void CopyListeReflexion(CodeStatementCollection method, ItemMap item, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, MappingModel model, Stack<RecurcifBag> queue, VariableExp liste, Type SourceType, Type DestinationType)
        {

            var methode = method.If(Helper.IsNotNull(sourceProperty));

            // target.PropertyName = new List<T>(); 
            if (liste.Type.Equals(targetProperty.Type))
                methode.TrueStatements.Set(targetProperty, liste.New());
            else            
                methode.TrueStatements.Set(targetProperty, new CodeCastExpression(targetProperty.Type, liste.New()));
            
            VariableExp current = null;

            TypeExpression myTypeEnumSource = GetSubType(sourceProperty.Type.TypeExpression, item.SourceItemType);

            //foreach (T current in source.PropertyName)
            var f2 = methode.TrueStatements.ForEach(myTypeEnumSource, sourceProperty, out current, "_cur" + myTypeEnumSource.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());
            var f11 = f2.Statements.If(Helper.IsNotNull(current));

            // copy conditionnelle
            CodeConditionStatement f1 = null;
            if (!string.IsNullOrEmpty(item.ConditionnalMappingMethod.Trim()))
                f1 = f11.TrueStatements.If(new MethodExp(Helper.This(), item.ConditionnalMappingMethod.Trim()).Call(current));
            else
                f1 = f11;


            // valeur simples comme enum, int, long, string Ect ...
            if (IsSimpleValue(SourceType) && IsSimpleValue(DestinationType))
            {
                if (SourceType == DestinationType)
                {
                    if (targetProperty.Type.TypeExpression.Equals(liste.Type.TypeExpression))
                        f1.TrueStatements.Add(targetProperty.Method("Add").Call(current));
                    else
                        f1.TrueStatements.Add(targetProperty.As(liste.Type).Method("Add", liste.Type).Call(current));
                }
                else
                {

                    // voir pour le cast

                    TypeExpression myTypeEnumTarget = GetSubType(targetProperty.Type.TypeExpression, item.TargetItemType);

                    PropertyExp target2 = new PropertyExp
                        (
                            targetProperty,
                            targetProperty.PropertyName + _cnt.ToString(),
                            myTypeEnumTarget
                        );
                    f1.TrueStatements.Declare(target2);


                    // voir pour le cast


                    if (targetProperty.Type.TypeExpression.Equals(liste.Type.TypeExpression))
                        f1.TrueStatements.Add(targetProperty.Method("Add").Call(target2));
                    else
                        f1.TrueStatements.Add(targetProperty.As(liste.Type).Method("Add", liste.Type).Call(target2));

                }

            }
            else
            {
                methode.TrueStatements.Add(new CodeCommentStatement(new CodeComment("Cannot find the good mapper")));
            }


        }


        public static Type[] types = new Type[] 
            { typeof(string), typeof(sbyte), typeof(byte), typeof(char)
              , typeof(short), typeof(ushort), typeof(int), typeof(uint)
              , typeof(long), typeof(ulong) , typeof(float), typeof(double)
              , typeof(Int16), typeof(Int32), typeof(Int64)                
              , typeof(UInt16), typeof(UInt32), typeof(UInt64)
              , typeof(bool), typeof(decimal)
              , typeof(sbyte?), typeof(byte?), typeof(char?)
              , typeof(short?), typeof(ushort?), typeof(int?), typeof(uint?)
              , typeof(long?), typeof(ulong?) , typeof(float?), typeof(double?)
              , typeof(Int16?), typeof(Int32?), typeof(Int64?)                
              , typeof(UInt16?), typeof(UInt32?), typeof(UInt64?)
              , typeof(bool?), typeof(decimal?)
            };

        private bool IsSimpleValue(Type type)
        {

            if (type == null)
                return false;

            if (type.IsEnum || types.Contains(type))
                return true;

            return false;
        }


        private void CreateEnumerator(CodeStatementCollection method, Mapper mapper, MappingModel model, VariableExp source, VariableExp target)
        {

            //Mapper mapper2 = model.FindMapperFor(source.Type.TypeExpression.AssemblyQualifiedName, target.Type.TypeExpression.AssemblyQualifiedName);
            Mapper mapper2 = model.FindMapperFor(mapper.MappingSource.TypeElement.ItemType, mapper.MappingDestination.TypeElement.ItemType);
            if (mapper2 == null)
            {
                method.Add(new CodeCommentStatement(new CodeComment("Cannot find the good mapper")));
                return;
            }

            //VariableExp map2 = new VariableExp(mapper2.Name, TypeExp.Object);

            TypeExpression myTypeEnum = GetSubType(source.Type.TypeExpression, mapper.MappingSource.TypeElement.ItemType);

            VariableExp current = null;
            var f2 = method.ForEach(myTypeEnum, source, out current, "_cur" + myTypeEnum.Name + _cnt.ToString(), "enumerator" + _cnt.ToString());
            var f1 = f2.Statements.If(Helper.IsNotNull(current));

            myTypeEnum = GetSubType( target.Type.TypeExpression, mapper.MappingDestination.TypeElement.ItemType);
            VariableExp v1 = f1.TrueStatements.Declare("itemTarget" + _cnt.ToString(), myTypeEnum, MyThis.Method(mapper2.MapMethodName, target.Type).Call(current));

            CodeExpression m = null;
            if (mapper.MappingSource.TypeElement.IsInterface || mapper.MappingSource.TypeElement.IsAbstract)
            {

                //ITemplateModel template
                //    = GetTemplate(TemplateExist(mapper.ModelTemplate)
                //    ? mapper.ModelTemplate
                //    : "TemplateModelDefault");

                ITemplateModel template = GetTemplate("TemplateModelDefault");

                template.Type = target.Type.TypeExpression;
                template.Parameter = "";
                template.MapperName = mapper.Name;
                m = template.InsertItem(v1, target);

            }

            else
            {
                //string methodName = string.IsNullOrEmpty(mapper.ModelTemplateParameter) ? "Add" : mapper.ModelTemplateParameter;
                string methodName = "Add";
                m = target.Method(methodName, target.Type).Call(v1);

            };

            f1.TrueStatements.Add(m);

        }

        private void CreateConverterMapClass(CodeStatementCollection method, ItemMap item, MappingModel model, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty, Dictionary<string, TypeExp> ListeConverters, Stack<RecurcifBag> queue)
        {



            Mapper mapper = model.FindMapperFor(item.SourceType, item.TargetType);


            if (mapper == null)
            {
                method.Add(new CodeCommentStatement(new CodeComment("Missing mapper for convert")));
                method.Add(new CodeCommentStatement(new CodeComment(item.SourceType)));
                method.Add(new CodeCommentStatement(new CodeComment("in")));
                method.Add(new CodeCommentStatement(new CodeComment(item.TargetType)));
                return;
            }

            // mapper.Name
            //VariableExp map2 = new VariableExp(AutoMapperName, TypeExp.Object);

            CodeStatementCollection Cde = null;

            if (!item.SourceIsValueType)
            {

                var If = method.If(Helper.IsNotNull(sourceProperty));
                Cde = If.TrueStatements;

                if (item.NullPolicy == PolicyNullValue.ReturnNewItem)
                {
                    VariableExp target21 = new VariableExp(target.VariableName + _cnt.ToString(), targetProperty.Type.TypeExpression);
                    CodeExpression expr = CreateTarget(mapper, sourceProperty, target21);
                    If.FalseStatements.Set(targetProperty, expr);
                }

            }
            else
            {
                Cde = method;
            }

            bool recursif =  QueueContains(queue, mapper);
            if (!recursif)
            {

                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- Start Non recursif {0} ({1})--------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));


                string nameType = sourceProperty.Type.TypeExpression.AssemblyQualifiedName;
                
                VariableExp target2 = new VariableExp(target.VariableName + _cnt.ToString(), targetProperty.Type.TypeExpression);
                CodeExpression expr = CreateTarget(mapper, sourceProperty, target2);

                Cde.Declare(target2, expr);


                if (model.Instrumentation)
                {
                    var If3 = Cde.If(Helper.IsNotNull(PerCounterClass));
                    If3.TrueStatements.Add(PerCounterIncrementClass);
                }




                CreateMap(Cde, mapper, ListeConverters, model, sourceProperty, target2, queue);
                
                if (model.Instrumentation)
                {
                    var If4 = Cde.If(Helper.IsNotNull(PerCounterClass));
                    If4.TrueStatements.Add(PerCounterDecrementClass);
                }
                Cde.Set(targetProperty, target2);



                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- End {0} ({1})----------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));

            }
            else
            {
                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- Start {0} ({1})--------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));

                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("HACK: Recurcif code detected {0}", ShowQueue(queue)))));
                var value = MyThis.Method(mapper.MapMethodName, targetProperty.Type).Call(sourceProperty);
                Cde.Set(targetProperty, value);

                Cde.Add(new CodeCommentStatement(new CodeComment(string.Format("-------------------- End {0} ({1})----------------------", mapper.Name, sourceProperty.Type.TypeExpression.Name))));

            }


            return;
        }

        private bool QueueContains(Stack<RecurcifBag> queue, Mapper mapper)
        {
            foreach (RecurcifBag r in queue)            
                if (r.Mapper == mapper)
                    return true;
            return false;
        }

        private string ShowQueue(Stack<RecurcifBag> queue)
        {

            RecurcifBag last = queue.Last();

            StringBuilder s = new StringBuilder();
            foreach (RecurcifBag item in queue)
            {
                s.AppendFormat("({0})", item.Mapper.Name);

                string t = item.Source.VariableName;
                s.Append(t);

                if (item != last)
                    s.Append(" > ");
            }

            return s.ToString();
        }

        #region Converters
        
        private TypeExp CreateTypeConverterMapProperty(CodeStatementCollection methode, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {

            CodeStatementCollection method = methode;

            if (item.SourceIsClass && !(sourceProperty.Type.TypeExpression.FullName == typeof(string).FullName))
            {
                var if1 = method.If(Helper.IsNotNull(sourceProperty));
                method = if1.TrueStatements;
            }
            else if (sourceProperty.PropertyName.Contains("."))
            {

                CodeConditionStatement if2 = null;
                string[] tb = sourceProperty.PropertyName.Split('.');
                CodeExpression source1 = sourceProperty.TargetObject;

                for (int i1 = 0; i1 < tb.Length - 1; i1++)
                {

                    source = new PropertyExp(source1, tb[i1], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source1));
                    method = if2.TrueStatements;

                }

            }

            TypeExp i = new TypeExp(item.Cast);
            string n = ConvertInCSharpName(i.TypeExpression.Name);
            var castObject = MyThis.Field("_C" + n, typeof(TypeConverter));

            string methodName = "ConvertFrom";


            method.SetWithCast(
                targetProperty,
                castObject.Method(methodName, targetProperty.Type).Call(sourceProperty)
            , targetProperty.Type
            );

            return i;
        }

        private TypeExp CreateIValueConverterMapProperty(CodeStatementCollection methode, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {


            CodeStatementCollection method = methode;

            if (item.SourceIsClass && !(sourceProperty.Type.TypeExpression.FullName == typeof(string).FullName))
            {
                var if1 = method.If(Helper.IsNotNull(sourceProperty));
                method = if1.TrueStatements;
            }
            else if (sourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source1 = sourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = sourceProperty.PropertyName.Split('.');
                for (int i1 = 0; i1 < tb.Length - 1; i1++)
                {

                    source = new PropertyExp(source1, tb[i1], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source1));
                    method = if2.TrueStatements;

                }

            }

            TypeExp i = new TypeExp(item.Cast);
            string n = ConvertInCSharpName(i.TypeExpression.Name);
            var castObject = MyThis.Field("_C" + n, "IValueConverter");

            string methodName = String.IsNullOrEmpty(item.CastWay) ? "Convert" : item.CastWay;
            var currentCulture = new CodeSnippetExpression("System.Globalization.CultureInfo.CurrentCulture");

            method.SetWithCast(
                targetProperty,
                castObject.Method(methodName, targetProperty.Type).Call
                (sourceProperty, 
                 i.TypeOf, 
                 currentCulture)
            , targetProperty.Type
            );

            return i;
        }

        private TypeExp CreateIConverterMapProperty(CodeStatementCollection methode, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {


            CodeStatementCollection method = methode;

            if (item.SourceIsClass && !(sourceProperty.Type.TypeExpression.FullName == typeof(string).FullName))
            {
                var if1 = method.If(Helper.IsNotNull(sourceProperty));
                method = if1.TrueStatements;
            }
            else if (sourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source1 = sourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = sourceProperty.PropertyName.Split('.');
                for (int i1 = 0; i1 < tb.Length - 1; i1++)
                {

                    source = new PropertyExp(source1, tb[i1], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source1));
                    method = if2.TrueStatements;

                }

            }


            TypeExp i = new TypeExp(item.Cast);

            string n = ConvertInCSharpName(i.TypeExpression.Name);
            var castObject = MyThis.Field("_C" + n, "IValueConverter");

            string methodName = String.IsNullOrEmpty(item.CastWay) ? "Convert" : item.CastWay;

            var currentCulture = new CodeSnippetExpression("System.Globalization.CultureInfo.CurrentCulture");

            method.SetWithCast
                (
                  targetProperty,
                  castObject.Method(methodName).Call(sourceProperty,
                  i.TypeOf,
                  currentCulture
                )
                , targetProperty.Type);

            return i;
        }

        private void CreateExplicitProperty(CodeStatementCollection methode, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {

            CodeStatementCollection method = methode;

            if (item.SourceIsClass && !(sourceProperty.Type.TypeExpression.FullName == typeof(string).FullName))
            {
                var if1 = method.If(Helper.IsNotNull(sourceProperty));
                method = if1.TrueStatements;
            }
            else if (sourceProperty.PropertyName.Contains("."))
            {

                CodeExpression source1 = sourceProperty.TargetObject;
                CodeConditionStatement if2 = null;
                string[] tb = sourceProperty.PropertyName.Split('.');
                for (int i = 0; i < tb.Length - 1; i++)
                {

                    source = new PropertyExp(source1, tb[i], typeof(object));
                    if2 = method.If(Helper.IsNotNull(source1));
                    method = if2.TrueStatements;

                }


            }

            System.CodeDom.CodeExpression s = null;       

            if (TryGetSpecificCast(sourceProperty, targetProperty, item, out s))
                method.Set(targetProperty, s);

            else
                method.SetWithCastIfNeed(targetProperty, sourceProperty);

        }

        private void CreateCustomMapProperty(CodeStatementCollection method, ItemMap item, VariableExp source, VariableExp target, PropertyExp sourceProperty, PropertyExp targetProperty)
        {

            string name = string.IsNullOrEmpty(item.CustomMethodName)
            ? ConvertInCSharpName("C" + (sourceProperty.Type.TypeExpression.Name + "To" + targetProperty.Type.TypeExpression.Name))
            : item.CustomMethodName;

            CodeExpression retour = null;

            if (sourceProperty.PropertyName == "this")            
                retour = MyThis.Method(name, targetProperty.Type).Call(source, target.As(new TypeExp(TypeExp.Object)));            
            else            
                retour = MyThis.Method(name, targetProperty.Type).Call(sourceProperty, target.As(new TypeExp(TypeExp.Object)));


            // cast le retour dans le type attendu.
            if (item.CastReturnCustomMethod)
                method.Set(targetProperty, Helper.Cast(retour, targetProperty.Type));
            else
                method.Set(targetProperty, retour);

        }

        #endregion

        private CodeExpression CreateTarget(Mapper mapper, CodeExpression source, VariableExp target)
        {

            CodeExpression m;
            var e = mapper.MappingDestination.TypeElement;

            string nname = "Create";
            if (!string.IsNullOrEmpty(mapper.CustomConstructorMethodName))
                nname = mapper.CustomConstructorMethodName;

            if (mapper.CustomConstructor)
            {
                m = MyThis.Method(nname, target.Type).Call(source);
            }
            else
            {
                if (e.IsInterface || e.IsAbstract)
                {
                    //if (!TemplateExist(mapper.ModelTemplate))
                    //{
                        m = MyThis.Method(nname, target.Type).Call(source);
                    //}
                    //else
                    //{
                    //    ITemplateModel template = GetTemplate(mapper.ModelTemplate);
                    //    template.Type = target.Type.TypeExpression;
                    //    template.MapperName = mapper.Name;
                    //    template.Parameter = mapper.ModelTemplateParameter;
                    //    m = template.CreateObject();
                    //}
                }
                else
                    m = new CodeObjectCreateExpression(target.Type.TypeExpression.ToString());

            }
            return m;
            
        }

        private CodeExpression CreateEnumerable(Mapper mapper, VariableExp target)
        {

            CodeExpression m;
            var e = mapper.MappingDestination.TypeElement;

            ITemplateModel template = GetTemplate("TemplateModelList");
                template.Type = target.Type.TypeExpression;
                template.MapperName = mapper.Name;
                template.Parameter = ""; // mapper.ModelTemplateParameter;
                m = template.CreateObject();

                return m;
        }

        public bool TemplateExist(string modelTemplate)
        {
            if (string.IsNullOrEmpty(modelTemplate))
                return false;
            List<string> lst = typeof(ITemplateModel).Assembly.GetTypes()
                    .Where(c =>
                        c.IsClass &&
                        !c.IsAbstract &&
                        c.IsOfType(false, typeof(ITemplateModel)
                        )
                ).Select(d => d.Name).ToList();
            return (lst.Contains(modelTemplate));
        }

        private ITemplateModel GetTemplate(string modelTemplate)
        {
            Type t = typeof(ITemplateModel).Assembly.GetTypes()
                    .Where(c =>
                        c.IsClass &&
                        !c.IsAbstract &&
                        c.IsOfType(false, typeof(ITemplateModel)) &&
                        c.Name == modelTemplate                        
                ).FirstOrDefault();

            return (ITemplateModel)Activator.CreateInstance(t);
        }

        private TypeMember CreateClass(Mapper mapper)
        {

            TypeMember Class = Helper.CreateClass(AutoMapperName, true, false);

            Class.IsPartial = true;

            return Class;
        }



        private Type FindType(string typeName)
        {


            if (_dicTypes.ContainsKey(typeName))
                return _dicTypes[typeName];

            Type type = null;

            if (!typeName.Contains("["))
            {

                foreach (Type item in ContainerTypes.Types)
                    if (item.AssemblyQualifiedName == typeName)
                    {
                        type = item;
                        break;
                    }

                if (type == null)
                    type = Type.GetType(typeName);

            }
            else
            {
                var e = (TypeExpression)typeName;
                type = e.Evaluate(c => FindType(c));
            }

            _dicTypes.Add(typeName, type);

            return type;
        }


        private Dictionary<string, Type> _dicTypes = new Dictionary<string, Type>();


        private const string Pattern1 = @"[^\w\d]*";
        private static Regex regEx = new Regex(Pattern1, RegexOptions.Compiled);
        //private CSharpParser.OutputWriter ws;
        public static string ConvertInCSharpName(string i) { return regEx.Replace(i, string.Empty); }


        public CSharpParser.OutputWriter writer { get; set; }

        public CSharpParser.OutputWriter _writer { get; set; }
    }

    [System.Diagnostics.DebuggerDisplay("{Mapper.Name}")]
    [CLSCompliant(false)]
    public class RecurcifBag
    {
        public Mapper Mapper { get; set; }

        public VariableExp Source { get; set; }

        public int Count { get; set; }
    }

    [CLSCompliant(false)]
    public class IndexBag
    {

        public List<ItemMap> Properties { get; set; }

        public VariableExp Variable { get; set; }
    }


}
